﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ServerApplication.Communication
{
    class CommunicationUtil
    {
        public static PeerManager.PeerManager mPeerManager;

        static void SetPeerManager(PeerManager.PeerManager pPeerManager)
        {
            mPeerManager = pPeerManager;
        }

        public static string GetLocalIPAddress()
        {
            var host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (var ip in host.AddressList)
            {
                //if (ip.AddressFamily == AddressFamily.InterNetwork)
                if (ip.IsIPv6LinkLocal == false && ip.AddressFamily == AddressFamily.InterNetwork)
                {

                    if (Convert.ToInt32(ip.ToString().Split('.')[0]) < 100)
                        return ip.ToString();
                }
            }
            throw new Exception("Local IP Address Not Found!");
        }


        public static Socket ConnectToClient(String ipAddr, int port)
        {
            try
            {
                //My ip address
                //Server's ip address
                IPAddress ipAddress = IPAddress.Parse(ipAddr);
                IPEndPoint remoteEp = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP  socket.
                Socket sender = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
                try
                {
                    //connect to server
                    sender.Connect(remoteEp);
                    Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());
                    return sender;
                }
                catch (Exception ee)
                {
                    Console.WriteLine("Error trying to connect to server. " + ee.ToString());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error whilst configuring stuff. " + e.ToString());
            }

            return null;
        }

        public static Socket BindToSocket(String ipAddr, int port)
        {
            IPAddress ipAddress = IPAddress.Parse(ipAddr);
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);

            // Create a TCP/IP socket.
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(10);

                return listener;
            }
            catch (Exception)
            {
                Console.WriteLine("Error While trying to bind to socket");
            }
            return null;
        }


        public static Socket ConnectToAClient(Socket listener)
        {
            try
            {
                Console.WriteLine("Waiting for a connection...");
                // Program is suspended while waiting for an incoming connection.
                Socket handler = listener.Accept();
                return handler;
            }
            catch (Exception)
            {
                Console.WriteLine("Error While trying to Accept connection with client");
            }
            return null;
        }

        public static Boolean CloseConnection(Socket handler)
        {
            try
            {
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
                return true;
            }
            catch (Exception)
            {
                Console.WriteLine("Error while trying to close connection with client");
            }
            return false;
        }

        public static String ListenForDataFromClient(Socket handler)
        {
            // Data buffer for incoming data.
            byte[] bytes = new Byte[1024];
            string data = null;

            try
            {
                // An incoming connection needs to be processed.
                while (true)
                {
                    bytes = new byte[1024];
                    int bytesRec = handler.Receive(bytes);
                    data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                    if (data.IndexOf("<EOF>") > -1)
                    {
                        break;
                    }
                }
                // Show the data on the console.
                Console.WriteLine("Text received : {0}", data);
                return data;
            }
            catch (Exception)
            {
                Console.WriteLine("Error while trying to recieve data from client");
            }
            return null;
        }

        public static Boolean SendMessageToClient(Socket handler, String message)
        {
            try
            {
                // Echo the data back to the client.
                byte[] msg = Encoding.ASCII.GetBytes(message);

                handler.Send(msg);
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            return false;
        }

        public static void ListenOnNetwork()
        {
            string ipAddress = GetLocalIPAddress();
            Socket listener = BindToSocket(ipAddress, 11000);
         
            try
            {
                while (true)
                {
                    Socket handler = ConnectToAClient(listener);
                    String data = ListenForDataFromClient(handler);
                    CloseConnection(handler);
                    InterpretMessage(data);
                }
            }
            catch (Exception)
            {
                Console.WriteLine("Error while trying to connect to client/send message/receive message");
            }
        }

        //---------------------------------
        // 
        private static bool SendCommandClient(PeerManager.Peer pCurrentPeer, int p_command)
        {
            try
            {
                Socket connectionToServer = ConnectToClient(pCurrentPeer.IpAddress.ToString(), pCurrentPeer.PortNumber);
                SendMessageToClient(connectionToServer,Convert.ToString(p_command)+"<EOF>");
                CloseConnection(connectionToServer);
                Console.WriteLine(pCurrentPeer.ToString()+ "Sending to current peer  ");
            }
            catch (Exception e)
            {
                return false;
            }

            return true;
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary>   Listen on network. and add new peers </summary>
        ///
        /// <remarks>   Adrian, 11/11/2015. </remarks>
        ///
        /// <param name="pManager"> The manager. </param>
        ///-------------------------------------------------------------------------------------------------

        public static void SendToPeers(List<PeerManager.Peer> pPeers, ServerApplication.Communication.UtilCommunication.CommandsToSendList mMessage)
        {
            foreach(PeerManager.Peer currentPeer in pPeers)
            {
                try
                {
                    bool succesCommand = SendCommandClient(currentPeer, (int)mMessage);
                }
                catch(Exception ex)
                {
                    Console.WriteLine("Error occured when commuicating with all the peers");
                }
            }
        }


        static void InterpretMessage(string p_command)
        {
            string[] commandParts = p_command.Split('|');

            string[] instructionParts = commandParts[0].Split(',');
            string peerName = commandParts[1].Split(new string[]{"<EOF>"}, StringSplitOptions.RemoveEmptyEntries)[0];

            int parsedCommand = 0;
            int.TryParse(instructionParts[0], out parsedCommand);

            switch(parsedCommand)
            {
                case 0:
                    mPeerManager.ChangeStatus(peerName);
                    break;

                case 1:
                    mPeerManager.ChangeStatus(peerName);
                    break;

                //introduction
                case 2:
                    string commDetails = instructionParts[1];
                    mPeerManager.AddPeer(commDetails, peerName);
                    break;
            }
        }

    }
}
